using System.Collections.Generic;
using UnityEngine;

using Prota.Unity;
using System;
using System.Linq;
using UnityEditor;

namespace Prota.Unity
{
    [ExecuteAlways]
    [RequireComponent(typeof(MeshFilter))]
    public class ProceduralMesh : MonoBehaviour
    {
        public Color vertexColor = Color.white;
        
        public Vector3[] vertices = Array.Empty<Vector3>();
        public int[] triangles = Array.Empty<int>();
        
        // ====================================================================================================
        // ====================================================================================================
        
        [InitializeOnLoadMethod]
        static void Init()
        {
            if(Application.isEditor && !Application.isPlaying)
            {
                foreach(var s in Resources.FindObjectsOfTypeAll<ProceduralMesh>())
                    s.RebuildMesh();
            }
        }
        
        [NonSerialized, Readonly] MeshFilter _meshFilter = null;
        public MeshFilter meshFilter
            => _meshFilter == null ? _meshFilter = this.GetComponent<MeshFilter>() : _meshFilter;
        
        
        [NonSerialized, Readonly] Mesh _mesh = null;
        public Mesh mesh => _mesh == null ? _mesh = new Mesh() { name = "ProceduralMesh" } : _mesh;
        
        void Awake()
        {
            meshFilter.mesh = mesh;
            RebuildMesh();
        }
        
        void Update()
        {
            if(Application.isPlaying) return;
            meshFilter.sharedMesh = mesh;
        }
        
        void OnEnable()
        {
            meshFilter.sharedMesh = mesh;
        }
        
        void OnDisable()
        {
            meshFilter.sharedMesh = null;
        }
        
        void OnDestroy()
        {
            DestroyImmediate(_mesh);
            _mesh = null;
        }
        
        void Reset()
        {
            vertices = new Vector3[] {
                new Vector3(-1, 1, 0),
                new Vector3(-1, -1, 0),
                new Vector3(1, -1, 0),
                new Vector3(1, 1, 0),
            };
            triangles = new int[] { 0, 1, 2, 2, 3, 0 };
            RebuildMesh();
        }
        
        static FlatList<Color> colors = new();
        void RebuildMesh(Mesh mesh, Vector3[] vertices, int[] triangles, Color color)
        {
            colors.Preserve(vertices.Length);
            mesh.Clear();
            mesh.SetVertices(vertices);
            mesh.SetTriangles(triangles, 0);
            colors.data.Fill(0, vertices.Length, color);
            mesh.SetColors(colors.data, 0, vertices.Length);
            mesh.RecalculateBounds();
        }
        
        public bool RebuildMesh()
        {
            if(vertices.IsNullOrEmpty()) Reset();
            
            RebuildMesh(mesh, vertices, triangles, vertexColor);
            
            if(this.TryGetComponent<PolygonCollider2D>(out var c))
            {
                c.usedByComposite = true;
                // c.compositeOperation = Collider2D.CompositeOperation.Intersect;
                if(Application.isPlaying) Destroy(c);
            }
            
            return true;
        }
        
        static List<Vector2> tempPolygon = new();
        static List<int> tempTriangles = new();
        public bool RebuildTriangleArray()
        {
            try
            {
                tempPolygon.AddRange(vertices.Select(v => v.ToVec2()));
                Prota.Unity.Algorithm.Triangulate(tempPolygon, tempTriangles);
                triangles = tempTriangles.ToArray();
            }
            finally
            {
                tempPolygon.Clear();
                tempTriangles.Clear();
            }
            return true;
        }

        public bool ProcessMeshBinding()
        {
            if(meshFilter.sharedMesh != mesh)
            {
                meshFilter.sharedMesh = mesh;
                return true;
            }
            return false;
        }
    
        // ====================================================================================================
        // ====================================================================================================
        
        public Vector3 VertexWorldPos(int i) => vertices[i.Repeat(vertices.Length)] + transform.position;
    
    }
}
